package com.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.bo.Conversation;
import com.bo.WxStreamBo;
import com.dao.GptHistoryDao;
import com.entity.GptHistoryEntity;
import com.enums.RoleEnum;
import com.google.common.collect.Lists;
import com.request.GptSendRequest;
import com.response.ConversationDTO;
import com.response.ConversationRes;
import com.response.WxyyResponse;
import com.service.GptService;
import com.utils.DateUtil;
import com.utils.SpringContextUtils;
import com.utils.TripleDTO;
import okhttp3.*;
import org.json.JSONObject;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static com.constant.SecretConstant.API_KEY;
import static com.constant.SecretConstant.SECRET_KEY;

@Service
public class GptServiceImpl implements GptService {

    @Resource
    OkHttpClient bdClient;

    @Resource
    GptHistoryDao gptHistoryDao;

    @Override
    public ConversationRes queryWxyy(GptSendRequest req, Long userId) throws IOException {
        ConversationRes res = new ConversationRes();
        res.setNeedRefresh(existContext(req.getContextId()));
        MediaType mediaType = MediaType.parse("application/json");
        List<Conversation> conversations = buildHistory(req.getContextId());
        conversations.add(new Conversation(RoleEnum.USER.getDesc(), req.getContent()));
        //todo 构建对话
        WxStreamBo wxStreamBo = new WxStreamBo(conversations);
        RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(wxStreamBo));
        Request request = new Request.Builder()
                .url("https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant?access_token=" + getAccessToken())
                .method("POST", body)
                .addHeader("Content-Type", "application/json")
                .build();
        Response response = bdClient.newCall(request).execute();
        WxyyResponse wxyyResponse = JSON.parseObject(response.body().string(), WxyyResponse.class);
        GptServiceImpl gptServiceImpl = SpringContextUtils.getBean("gptServiceImpl", GptServiceImpl.class);
        gptServiceImpl.createNewGptHistory(req, wxyyResponse, userId);
        List<ConversationDTO> conversationList = queryHistoryDTO(req.getContextId());
        res.setConversationList(conversationList.subList(conversationList.size() - 2, conversationList.size()));
        return res;
    }

    private boolean existContext(Long contextId) {
        EntityWrapper<GptHistoryEntity> wrapper = new EntityWrapper<>();
        wrapper.eq("context_id", contextId);
        return gptHistoryDao.selectCount(wrapper) == 0;
    }

    @Transactional
    public void createNewGptHistory(GptSendRequest req, WxyyResponse wxyyResponse, Long userId) {
        GptHistoryEntity entity1 = new GptHistoryEntity();
        entity1.setContextId(req.getContextId());
        entity1.setRole(RoleEnum.USER.getCode());
        entity1.setUserId(userId);
        entity1.setDetailInfo(req.getContent());
        gptHistoryDao.insert(entity1);

        GptHistoryEntity entity2 = new GptHistoryEntity();
        entity2.setContextId(req.getContextId());
        entity2.setRole(RoleEnum.ASSISTANT.getCode());
        entity2.setUserId(userId);
        entity2.setDetailInfo(wxyyResponse.getResult());
        gptHistoryDao.insert(entity2);
    }

    public List<Conversation> buildHistory(Long contextId) {
        EntityWrapper<GptHistoryEntity> wrapper = new EntityWrapper<>();
        wrapper.eq("context_id", contextId);
        List<GptHistoryEntity> historyList = gptHistoryDao.selectList(wrapper);
        if (CollectionUtils.isEmpty(historyList)) {
            return Lists.newArrayList();
        }
        return historyList.stream().map(entity -> new Conversation(RoleEnum.getEnumByCode(entity.getRole()).getDesc(), entity.getDetailInfo())).collect(Collectors.toList());
    }

    @Override
    public List<ConversationDTO> queryHistoryDTO(Long contextId) {
        EntityWrapper<GptHistoryEntity> wrapper = new EntityWrapper<>();
        wrapper.eq("context_id", contextId);
        List<GptHistoryEntity> historyList = gptHistoryDao.selectList(wrapper);
        if (CollectionUtils.isEmpty(historyList)) {
            return Lists.newArrayList();
        }
        return historyList.stream().map(entity -> new ConversationDTO(RoleEnum.getEnumByCode(entity.getRole()).getDesc(),
                entity.getDetailInfo(), DateUtil.timToStr(entity.getCreateTime()))).collect(Collectors.toList());
    }

    @Override
    public List<TripleDTO<String, String, List<Conversation>>> getUserQueryHistory(Long userId) {
        List<TripleDTO<String, String, List<Conversation>>> resList = new ArrayList<>();
        EntityWrapper<GptHistoryEntity> wrapper = new EntityWrapper<>();
        wrapper.eq("user_id", userId);
        wrapper.orderAsc(Lists.newArrayList("context_id"));
        List<GptHistoryEntity> historyList = gptHistoryDao.selectList(wrapper);
        Map<Long, List<GptHistoryEntity>> collect1 = historyList.stream().collect(Collectors.groupingBy(GptHistoryEntity::getContextId));
        for (Map.Entry<Long, List<GptHistoryEntity>> entry : collect1.entrySet()) {
            List<Conversation> collect = entry.getValue().stream().sorted(new Comparator<GptHistoryEntity>() {
                @Override
                public int compare(GptHistoryEntity o1, GptHistoryEntity o2) {
                    if (!o1.getCreateTime().before(o2.getCreateTime()) && !o1.getCreateTime().after(o2.getCreateTime())) {
                        return o1.getRole() == 1 ? -1 : 1;
                    }
                    return o1.getCreateTime().before(o2.getCreateTime()) ? -1 : 1;
                }
            }).map(entity -> new Conversation(RoleEnum.getEnumByCode(entity.getRole()).getDesc(), entity.getDetailInfo())).collect(Collectors.toList());
            resList.add(new TripleDTO<>(entry.getKey() + "", collect.get(0).getContent(), collect));
        }
        return resList;
    }

    @Override
    public Long getUserNewestContextId(Long userId) {
        EntityWrapper<GptHistoryEntity> wrapper = new EntityWrapper<>();
        wrapper.eq("user_id", userId);
        wrapper.orderDesc(Lists.newArrayList("context_id"));
        Optional<GptHistoryEntity> history = gptHistoryDao.selectList(wrapper).stream().findFirst();
        if (history.isPresent()) {
            return history.get().getContextId() + 1L;
        }
        return 1L;
    }


    private String getAccessToken() throws IOException {
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        RequestBody body = RequestBody.create(mediaType, "grant_type=client_credentials&client_id=" + API_KEY
                + "&client_secret=" + SECRET_KEY);
        Request request = new Request.Builder()
                .url("https://aip.baidubce.com/oauth/2.0/token")
                .method("POST", body)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .build();
        Response response = bdClient.newCall(request).execute();
        return new JSONObject(response.body().string()).getString("access_token");
    }
}
